
#include "../include/GPCompute.h"

#include <windows.h>
#include <MemMgr2D.h>

#define SCREEN_WIDTH		512		// texture is 512w
#define SCREEN_HEIGHT		512

#define TEXTURE_WIDTH		512
#define TEXTURE_HEIGHT		512

void DoCounter( HWND hWnd ) {
	static DWORD t;
	static LONG count;
	DWORD currtime = GetTickCount();
	if ( t==0 ) t = currtime;
	if ( t + 1000 < currtime ) { static int i;
		static char wndtxt[260], strfps[260];
		if (i==0) { i=1; GetWindowTextA( hWnd, wndtxt, MAX_PATH ); }
		wsprintfA( strfps, "%s - FPS:%d", wndtxt, count*1000/(currtime-t) );
		SetWindowTextA( hWnd, strfps );
		t += 1000; count = 0;
	}
	++count;
}

HRESULT LoadShader( LPDWORD* pdwShader, const char* filename )
{
	HANDLE p, f;
	p = CreateFileA( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( INVALID_HANDLE_VALUE == p ) {
		return E_FAIL;
	}
	f = CreateFileMappingA( p, NULL, PAGE_READONLY, 0, 0, NULL );
	if ( NULL == f ) {
		return E_FAIL;
	}
	*pdwShader = (LPDWORD)MapViewOfFile( f, FILE_MAP_READ, 0, 0, 0 );
	if ( NULL == *pdwShader ) {
		return E_FAIL;
	}
	CloseHandle( f );
	CloseHandle( p );
	return S_OK;
}
void UnloadShader( DWORD* pdwShader )
{
	UnmapViewOfFile( (LPCVOID)pdwShader );
}

#define MAXRECT			16
#define COLOR_UNALLOC	0xFF000000
#define COLOR_ALLOC(n)	0xFF000000 | colortable[n&0xF]

HGPGPU hgpgpu;
HMEM2D hmem2d;
HGPVRRAY hvrray;

int flagtable[MAXRECT] = {0};
MemRect memrc[MAXRECT];
int index = 0;

int wtable[] = { 16, 32, 48, 64, 96, 128, 192, 256, };
int htable[] = {  8, 16, 24, 32, 64,  96, 128, 192, };
DWORD colortable[] = {
	0xFF8080, 0xFFFF80, 0x80FF80, 0x00FF80, 
	0x80FFFF, 0x0080FF, 0xFF80C0, 0xFF80FF, 
	0xFF0000, 0xFFFF00, 0x80FF00, 0x00FF40, 
	0x0000FF, 0x0080C0, 0x8080C0, 0xFF00FF, 
};

static void FillRectColor( void* pBuffer, long pitch, MemRect const* rect, unsigned long color )
{
	int i, j;
	LPBYTE pData = (LPBYTE)pBuffer; // point to the line start 
	for ( i=0; i<rect->h; ++i ) {
		for ( j=0; j<rect->w; ++j ) {
			*((DWORD*)pData+j) = color;
		}
		pData += pitch;
	}
}

static void AppendNewRect( HGPVRRAY hvrray, MemRect const* memrc, unsigned long color )
{
	HGPARRAY harray; void* data; long pitch;
	int ret;
	// prepare resource 
	ret = GpComputeAllocate( hgpgpu, memrc->w, memrc->h, TYPE_COLORALPHA, PREC_IN_32, OPMODE_READ, &harray );
	if ( ret < 0 ) OutputDebugStringA( "Failed Allocate Memory!\n" );

	ret = GpComputeGetPointer( harray, &data, &pitch );
	if ( ret < 0 ) OutputDebugStringA( "Failed GetPointer!\n" );

	FillRectColor( data, pitch, memrc, color );

	ret = GpComputeSubmitEx( hgpgpu, harray, 0, 0, memrc->w, memrc->h, hvrray, memrc->x, memrc->y );
	if ( ret < 0 ) OutputDebugStringA( "Failed Submit VideoMemory!\n" );

	ret = GpComputeDeallocate( hgpgpu, harray );
	if ( ret < 0 ) OutputDebugStringA( "Failed Delete SysMemtexture!\n" );
}

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
		case WM_TIMER:
			if ( flagtable[index]==0 ) {
				// update time change the rectangle allocate
				memrc[index].x = memrc[index].y = 0;
				memrc[index].w = wtable[rand() & 0x7];
				memrc[index].h = htable[rand() & 0x7];
				if ( Mem2DAllocate( hmem2d, &memrc[index] ) > 0 ) {
					flagtable[index] = 3;
					AppendNewRect( hvrray, &memrc[index], COLOR_ALLOC(index) );
				}
			} else {
				--flagtable[index];
				if ( flagtable[index] <= 0 ) {
					AppendNewRect( hvrray, &memrc[index], COLOR_UNALLOC );
					Mem2DDeallocate( hmem2d, &memrc[index] );
				}
			}
			++index; if ( index >= MAXRECT ) index = 0;
			return 0;

        case WM_DESTROY:
            PostQuitMessage( 0 );
            return 0;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE _1, LPSTR _2, INT cmdShow )
{
	HWND hWnd; GP_CONTEXT ctx; RECT rect = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, };

	// Register the window class
    WNDCLASSEXA wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                      "GP Tutorial", NULL };
    RegisterClassExA( &wc );

    // Create the application's window
    hWnd = CreateWindowA( "GP Tutorial", "GP Tutorial 03: TexMgr",
                      WS_OVERLAPPEDWINDOW, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
                      GetDesktopWindow(), NULL, wc.hInstance, NULL );

	// adjust window size ... 
	AdjustWindowRect( &rect, WS_OVERLAPPEDWINDOW, FALSE );
	MoveWindow( hWnd, 300+rect.left, 300+rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );

	srand(2010);
	Mem2DInit(TEXTURE_WIDTH, TEXTURE_HEIGHT, -1, &hmem2d);

	ctx.width = SCREEN_WIDTH;
	ctx.height = SCREEN_HEIGHT;
	ctx.colortype = TYPE_COLORALPHA;
	ctx.colorprecision = PREC_IN_32;
	ctx.depthtype = TYPE_DEPTH;
	ctx.depthprecision = PREC_IN_16;
	ctx.canvas = (void*)hWnd;

    // Initialize Direct3D
    if( GpComputeInit( &hgpgpu, &ctx, 0 ) >= 0 )
    {
        MSG msg;

		int ret;
		GP_CALCSTATE state = {0}; 
		GP_RECT rc; HGPSHAPERECT vrc;
		DWORD vshid, pshid; LPDWORD p;

		// vertex prepare 
		rc.pt1.x = -1.0f, rc.pt1.y =  1.0f;
		rc.pt2.x =  1.0f, rc.pt2.y = -1.0f;
		ret = GpComputeShapeRect( hgpgpu, &rc, 1, &vrc );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create Vertex Rectangle!\n" );
		ret = GpComputeSelectSourceShape( hgpgpu, vrc );
		if ( ret < 0 ) OutputDebugStringA( "Failed SetStreamSource and Index!\n" );

		// texture prepare 
		ret = GpComputeAllocateVram( hgpgpu, TEXTURE_WIDTH, TEXTURE_HEIGHT, 
			TYPE_COLORALPHA, PREC_IN_32, OPMODE_READ, &hvrray );
		if ( ret < 0 ) OutputDebugStringA( "Failed Allocate VideoMemory!\n" );
		ret = GpComputeSelectSourceUnit( hgpgpu, hvrray, 0 ); 
		if ( ret < 0 ) OutputDebugStringA( "Failed SetTexture!\n" );
		
		// shader programe 
		if (FAILED(LoadShader( &p, "GPComputeTexMgr.vso" ))) 
			OutputDebugStringA( "Failed Allocate Load VertexShader!\n" );
		ret = GpComputeCreateProgram( hgpgpu, PROGRAM_VERTEX, &vshid, p );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create VertexShader!\n" );
		UnloadShader( p );
		ret = GpComputeSelectProgram( hgpgpu, PROGRAM_VERTEX, vshid );
		if ( ret < 0 ) OutputDebugStringA( "Failed Select VertexShader!\n" );

		if (FAILED(LoadShader( &p, "GPComputeTexMgr.pso" )))
			OutputDebugStringA( "Failed Allocate Load PixelShader" );
		ret = GpComputeCreateProgram( hgpgpu, PROGRAM_PIXEL, &pshid, p );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create PixelShader!\n" );
		UnloadShader( p );
		ret = GpComputeSelectProgram( hgpgpu, PROGRAM_PIXEL, pshid );
		if ( ret < 0 ) OutputDebugStringA( "Failed Select PixelShader!\n" );

		// render state : 
		state.red_write = state.green_write = state.blue_write = 1;
		ret = GpComputeSetProp( hgpgpu, &state );
		if ( ret < 0 ) OutputDebugStringA( "Failed Set RenderState!\n" );

		// Show the window
		ShowWindow( hWnd, cmdShow );
		UpdateWindow( hWnd );

        // Enter the message loop
        ZeroMemory( &msg, sizeof(msg) );

		// prepare the color : 
		memrc[index].x = memrc[index].y = 0;
		memrc[index].w = SCREEN_WIDTH;
		memrc[index].h = SCREEN_HEIGHT;
		AppendNewRect( hvrray, &memrc[index], COLOR_UNALLOC );
		
		SetTimer( hWnd, 0, 100, NULL );
        while( msg.message!=WM_QUIT )
        {
            if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
            {
                TranslateMessage( &msg );
                DispatchMessage( &msg );
            }
            else
            {
				GP_PREPARE prepare = {0};
				DoCounter( hWnd );
				// RENDER 
				ret = GpComputeBeginTarget( hgpgpu, NULL, &prepare );
				if ( ret < 0 ) OutputDebugStringA( "Failed BeginScene!\n" );
				ret = GpComputeCalculateRect( hgpgpu, 0, 0, NULL ); // draw all 
				if ( ret < 0 ) OutputDebugStringA( "Failed RenderToTexture!\n" );
				ret = GpComputeEndTarget( hgpgpu, 1 ); // with present
				if ( ret < 0 ) OutputDebugStringA( "Failed EndScene!\n" );
            }
        }
		KillTimer( hWnd, 0 );

		GpComputeDeleteProgram( hgpgpu, PROGRAM_PIXEL, pshid );
		GpComputeDeleteProgram( hgpgpu, PROGRAM_VERTEX, vshid );
		
		GpComputeDeallocateVram( hgpgpu, hvrray );

		GpComputeShapeFree( hgpgpu, vrc );

		GpComputeFree( hgpgpu );
    }

	Mem2DFree( hmem2d );

	DestroyWindow( hWnd );
    // Clean up everything and exit the app
    UnregisterClassA( "GP Tutorial", wc.hInstance );
    return 0;
}
